/**
 * @Title: WeixinPayImpl
 * @Package cn.com.yiqi.wsc.service.weixin.impl
 * @Description: TODO
 * Copyright: Copyright (c) 2016
 * Company: 成都壹柒互动科技有限公司
 * @author 会员系统组-林威
 * @date 2017/2/13 14:44
 * @version V1.0
 */
package cn.com.yiqi.wsc.service.weixin.impl;

import cn.com.artemis.common.util.MyGson;
import cn.com.artemis.common.util.OkHttpUtil;
import cn.com.yiqi.wsc.Ro.WeixinMessageRo;
import cn.com.yiqi.wsc.common.Result;
import cn.com.yiqi.wsc.common.ResultUtil;
import cn.com.yiqi.wsc.config.weixin.WeixinpayConfig;
import cn.com.yiqi.wsc.dao.customer.CustomerMentionDao;
import cn.com.yiqi.wsc.dao.order.OrderMainMapper;
import cn.com.yiqi.wsc.ro.WeixinInfoRO;
import cn.com.yiqi.wsc.ro.WeixinOrderRO;
import cn.com.yiqi.wsc.ro.WeixinTansferRO;
import cn.com.yiqi.wsc.service.base.BaseParaService;
import cn.com.yiqi.wsc.service.order.impl.OrderServiceImpl;
import cn.com.yiqi.wsc.service.weixin.WeixinPayService;
import cn.com.yiqi.wsc.Ro.BirthdayTaskDO;
import cn.com.yiqi.wsc.service.weixin.wxDo.TikekRO;
import cn.com.yiqi.wsc.service.weixin.wxDo.WxSignDO;
import cn.com.yiqi.wsc.util.*;
import com.google.gson.reflect.TypeToken;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.security.KeyStore;
import java.util.*;

/**
 * @author 会员系统组-林威
 * @ClassName: WeixinPayImpl
 * Description: TODO
 * @date 2017/2/13 14:44
 */
@Service
public class WeixinPayImpl implements WeixinPayService {

    @Autowired
    WeixinpayConfig weixinpayConfig;

    @Autowired
    OrderMainMapper orderDao;

    @Autowired
    OrderServiceImpl orderService;

    @Autowired
    BaseParaService baseParaService;

    @Autowired
    CustomerMentionDao mentionDao;
    
    @Value("#{APP_PROP['birthday.subject.url']}")
    String subjectUrl;

    /**
     * 微信支付请求参数
     */
    public Result precreate(WeixinOrderRO orderVO) {
        Result re = new Result();

        //计算sign
        if (orderVO == null) {
            return null;
        }
        Map<String, String> map = new HashMap<>();
        BeanMap beanMap = BeanMap.create(orderVO);
        for (Object key : beanMap.keySet()) {
            if (null != beanMap.get(key)) {
                map.put(key + "", beanMap.get(key).toString());
            }
        }
        orderVO.setSign(createSign(map));

        Map<String, String> data = null;
        String url = weixinpayConfig.getPrecreate_Url();
        MediaType type = MediaType.parse("text/xml");
        RequestBody requestBody = RequestBody.create(type, toXml(orderVO));
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        try {
            Response response = OkHttpUtil.execute(request);
            if (response.isSuccessful()) {
                data = XmlUtils.xmlBody2map(response.body().string(), "xml");
                //验证签名
                if (validateSign(data)) {
                    //生成调启支付请求参数
                    Map<String, String> mapPay = new HashMap<>();
                    mapPay.put("appId", weixinpayConfig.getAppid());
                    //mapPay.put("partnerid",weixinpayConfig.getMch_id());
                    mapPay.put("package", "prepay_id=" + data.get("prepay_id"));
                    // mapPay.put("package","Sign=WXPay");
                    mapPay.put("signType", "MD5");
                    mapPay.put("nonceStr", RandomStringUtils.randomAlphanumeric(20));
                    mapPay.put("timeStamp", new Date().getTime() / 1000 + "");
                    mapPay.put("sign", createSign(mapPay));
                    re.putData("data", mapPay);
                    return ResultUtil.success(re);
                }
            } else {
                throw new IOException("Unexpected code " + response);
            }
        } catch (Exception e) {
            return null;
        }
        return ResultUtil.fail(re, "参数错误");
    }

    /**
     * 查询订单是否已经支付，如果已支付，修改本地状态'
     *
     * @Param orderEntity
     */
    public Boolean searchOrder(String orderCode) {
        Map<String, String> data = new HashMap<>();
        data.put("appid", weixinpayConfig.getAppid());
        data.put("mch_id", weixinpayConfig.getMch_id());
        data.put("out_trade_no", orderCode);
        data.put("nonce_str", RandomStringUtils.randomAlphanumeric(20));
        String sign = createSign(data);
        data.put("sign", sign);
        String url = "https://api.mch.weixin.qq.com/pay/orderquery";
        MediaType type = MediaType.parse("text/xml");
        RequestBody requestBody = RequestBody.create(type, toXml(data));
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        try {
            Response response = OkHttpUtil.execute(request);
            if (response.isSuccessful()) {
                data = XmlUtils.xmlBody2map(response.body().string(), "xml");
                //验证签名
                if (validateSign(data)) {
//                    SUCCESS—支付成功REFUND—转入退款
//                    NOTPAY—未支付
//                    CLOSED—已关闭
//                    REVOKED—已撤销（刷卡支付）
//                    USERPAYING--用户支付中
//                    PAYERROR--支付失败(其他原因，如银行返回失败)
                    if (null != data.get("trade_state") && data.get("trade_state").equals("SUCCESS")) {
                        //TODO 更新order状态
                        /*orderService.update(orderEntity.getId(),
                                OrderStatus.TRADE_SUCCESS,
                                DateUtil.stringToDate("yyyyMMddHHmmss",data.get("time_end")),
                                data.get("transaction_id"));*/
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            return false;
        }
        return false;
    }

    /**
     * 付款给关注者微信号
     *
     * @param openId  商户appid下，某用户的openid
     * @param amount  打款金额 单位分 最低1元(即100分)
     * @param tradeNo 订单号，需保证唯一
     * @param desc    转账描述
     **/
    public Result transfer(String openId, int amount, String tradeNo, String desc) {
        Result re = new Result();
        WeixinTansferRO ro = new WeixinTansferRO();
        ro.setMch_appid(weixinpayConfig.getAppid());
        ro.setMchid(weixinpayConfig.getMch_id());
        ro.setNonce_str(RandomStringUtils.randomAlphanumeric(20));
        ro.setPartner_trade_no(tradeNo);
        ro.setOpenid(openId);
        ro.setAmount(amount);
        ro.setDesc(desc);
        ro.setSpbill_create_ip(getIp());
        Map<String, String> map = new HashMap<>();
        BeanMap beanMap = BeanMap.create(ro);
        for (Object key : beanMap.keySet()) {
            if (null != beanMap.get(key)) {
                map.put(key + "", beanMap.get(key).toString());
            }
        }
        ro.setSign(createSign(map));
        String xmlStr = toXml(ro);
        try {
            String xmlResult = excute(xmlStr);
            Map<String, String> returnMap;
            returnMap = XmlUtils.xmlBody2map(xmlResult, "xml");
            if (returnMap != null && returnMap.get("return_code").equals("SUCCESS") && returnMap.get("result_code").equals("SUCCESS")) {
                re.setData(returnMap);
                return re;
            } else {
                return ResultUtil.fail(re, returnMap.get("err_code_des"));
            }
        } catch (Exception e) {
            return ResultUtil.fail(re, "system error!");
        }

    }


    private String excute(String xmlStr) throws Exception {
        FileInputStream instream = null;
        CloseableHttpResponse response = null;
        CloseableHttpClient httpclient = null;
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            instream = new FileInputStream(new File(weixinpayConfig.getCertPath()));

            keyStore.load(instream, weixinpayConfig.getMch_id().toCharArray());

            // Trust own CA and all self-signed certs
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, weixinpayConfig.getMch_id().toCharArray())
                    .build();
            // Allow TLSv1 protocol only
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[]{"TLSv1"},
                    null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();


            //HttpGet httpget = new HttpGet("https://api.mch.weixin.qq.com/secapi/pay/refund");
            HttpPost myPost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers");
            StringEntity myEntity = new StringEntity(xmlStr, "UTF-8");
            myPost.addHeader("Content-Type", "text/xml");
            myPost.setEntity(myEntity);
            response = httpclient.execute(myPost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                String xmlRturn = "";
                String text;
                while ((text = bufferedReader.readLine()) != null) {
                    xmlRturn += text;
                }
                return xmlRturn;
            }
            EntityUtils.consume(entity);

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            instream.close();
            response.close();
            httpclient.close();
        }
        return null;
    }


    private String getIp() {
        return "171.221.150.5";
       /* try {
            Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
            InetAddress ip = null;
            while (allNetInterfaces.hasMoreElements()) {
                NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
                Enumeration addresses = netInterface.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    ip = (InetAddress) addresses.nextElement();
                    if (ip != null && ip instanceof Inet4Address) {

                        return ip.getHostAddress();
                    }
                }
            }
        } catch (Exception e) {
            return null;
        }
        return null;*/
    }


    /**
     * 微信支付回调
     */
    public String notifyHook(HttpServletRequest request) throws Exception {
        String str = setXml("FAIL", null);
        request.setCharacterEncoding("UTF-8");
        InputStream in = request.getInputStream();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
        out.close();
        in.close();
        String msgxml = new String(out.toByteArray(), "utf-8");//xml数据
        Map<String, String> map = XmlUtils.xmlBody2map(msgxml, "xml");
        //验证签名 通信成功
        if (validateSign(map) && map.get("return_code").equals("SUCCESS")) {
            str = setXml("SUCCESS", null);
            // 支付业务成功
            if (map.get("result_code").equals("SUCCESS")) {
                //TODO 更新订单
                String openid = map.get("openid");
                String outTradeNo = map.get("out_trade_no");
                String tradeNo = map.get("transaction_id");
                String dateParttern = "yyyyMMddHHmmss";
                Date paidDate = DateUtil.stringToDate(dateParttern, map.get("time_end"));
                orderService.orderPaySuccess(outTradeNo, tradeNo, paidDate, openid);
            }
        }
        return str;
    }

    private String setXml(String return_code, String return_msg) {
        String str = "<xml><return_code><![CDATA[" + return_code + "]]></return_code>";
        if (null != return_msg) {
            str = str + "<return_msg><![CDATA[" + return_msg + "]]></return_msg>";
        }
        str += "</xml>";
        return str;
    }

    private String toXml(Map<String, String> map) {
        //转xml字符串
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        for (Object key : map.keySet()) {
            if (null != map.get(key)) {
                sb.append("<" + key + ">");
                sb.append(map.get(key));
                sb.append("</" + key + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    private String toXml(Object orderVO) {
        //转xml字符串
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        BeanMap beanMap = BeanMap.create(orderVO);
        for (Object key : beanMap.keySet()) {
            if (null != beanMap.get(key)) {
                sb.append("<" + key + ">");
                sb.append(beanMap.get(key));
                sb.append("</" + key + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 验证签名
     */
    private boolean validateSign(Map<String, String> map) {
        //生成微信签名
        String signRetrun = map.get("sign");
        //验证签名
        String sign = createSign(map);
        if (sign.equals(signRetrun)) {
            return true;
        }
        return false;

    }

    /**
     * 生成微信sign
     */
    private String createSign(Map<String, String> map) {
        List<String> list = new ArrayList<>(map.keySet());
        Collections.sort(list);
        String keyValueStr = "";
        for (String key : list) {
            if (null != map.get(key) && !"sign".equals(key)) {
                keyValueStr += key + "=" + map.get(key) + "&";
            }
        }
        keyValueStr = keyValueStr + "key=" + weixinpayConfig.getKey();
        return MD5Util.paseMD5(keyValueStr).toUpperCase();
    }


    /**
     * 获取授权url
     *
     * @Param state 自定义参数
     */
    public String authUrl(String state) {
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
                "appid=" + weixinpayConfig.getAppid() +
                "&redirect_uri=" + weixinpayConfig.getRedirectUrl() +
                "&response_type=code" +
                "&state=" + state +
                "&scope=snsapi_userinfo#wechat_redirect";
        return url;
    }

    /**
     * 获取授权数据
     */
    public Map<String, Object> access_token(String code) {
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                "appid=" + weixinpayConfig.getAppid() +
                "&secret=" + weixinpayConfig.getAppsecret() +
                "&code=" + code +
                "&grant_type=authorization_code";
          /* access_token	网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
                  expires_in	access_token接口调用凭证超时时间，单位（秒）
                  refresh_token	用户刷新access_token
                  openid	用户唯一标识，请注意，在未关注公众号时，用户访问公众号的网页，也会产生一个用户和公众号唯一的OpenID
                  scope	用户授权的作用域，使用逗号（,）分隔
                  unionid	只有在用户将公众号绑定到微信开放平台帐号后，才会出现该字段。详见：获取用户个人信息（UnionID机制）*/
        return sendGet(url);

    }

    /**
     * 获取token（无需用户授权接口）
     */
    public String gettoken() {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
                "&appid=" + weixinpayConfig.getAppid() +
                "&secret=" + weixinpayConfig.getAppsecret();
          /* access_token 	获取到的凭证
           expires_in 	凭证有效时间，单位：秒 */
        Request request = new Request.Builder().url(url).build();
        try {
            Response response = OkHttpUtil.execute(request);
            if (response.isSuccessful()) {
                Map<String, Object> map = GsonUtil.getGson().fromJson(response.body().string(), new TypeToken<Map<String, Object>>() {
                }.getType());
                if (map != null && map.get("access_token") != null) {
                    return map.get("access_token").toString();
                } else {
                    return null;
                }
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }


    /**
     * 刷新token
     */
    public Map<String, Object> refresh_token(String refresh_token) {
        String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?" +
                "appid=" + weixinpayConfig.getAppid() +
                "&grant_type=refresh_token" +
                "&refresh_token=" + refresh_token;
        return sendGet(url);
    }

    /**
     * 获取信息(无需关注也能获取信息)
     */
    public WeixinInfoRO getwxUserInfo(String access_token, String openid) {

        //此接口无需关注也能获取信息
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "" +
                "&openid=" + openid +
                "&lang=zh_CN";
        return sendWxinfoget(url);

    }

    /**
     * 获取信息(需关注才可获取信息)
     */
    public WeixinInfoRO getwxUserInfoAttention(String access_token, String openid) {

        //此接口无需关注也能获取信息
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + access_token + "" +
                "&openid=" + openid +
                "&lang=zh_CN";
        return sendWxinfoget(url);
    }

    private WeixinInfoRO sendWxinfoget(String url) {
        WeixinInfoRO weixinInfo = new WeixinInfoRO();
        Request request = new Request.Builder().url(url).build();
        try {
            Response response = OkHttpUtil.execute(request);
            if (response.isSuccessful()) {
                weixinInfo = GsonUtil.getGson().fromJson(response.body().string(), WeixinInfoRO.class);
                return weixinInfo;
            }
        } catch (Exception e) {
            return weixinInfo;
        }
        return weixinInfo;
    }

    private Map<String, Object> sendGet(String url) {
        Request request = new Request.Builder().url(url).build();
        try {
            Response response = OkHttpUtil.execute(request);
            if (response.isSuccessful()) {
                Map<String, Object> map = GsonUtil.getGson().fromJson(response.body().string(), new TypeToken<Map<String, Object>>() {
                }.getType());
                if (map != null && map.get("openid") != null) {
                    return map;
                } else {
                    return null;
                }
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }


    /**
     * 微信js授权
     */
    public WxSignDO getSignature(String url, String tiket) {
        WxSignDO wxSignDO = new WxSignDO();
        wxSignDO.setAppId(weixinpayConfig.getAppid());

        Map<String, String> params = new HashMap<>();
        String nonceStr = UUID.randomUUID().toString();
        wxSignDO.setNonceStr(nonceStr);
        String timeStamp = Long.toString(System.currentTimeMillis() / 1000);
        wxSignDO.setTimeStamp(timeStamp);
        params.put("noncestr", nonceStr);
        params.put("jsapi_ticket", tiket);
        params.put("timestamp", timeStamp);
        params.put("url", url);
        wxSignDO.setSignature(WxApiUtil.sha1Sign(params));
        return wxSignDO;
    }

    /**
     * 获取tikect
     */
    public String getTiket(String access_token) {
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi";
        Request request = new Request.Builder().url(url).build();

        try {
            Response response = OkHttpUtil.execute(request);
            if (response.isSuccessful()) {
                TikekRO ro = GsonUtil.getGson().fromJson(response.body().string(), TikekRO.class);
                if (StringUtils.isNotEmpty(ro.getTicket())) {
                    return ro.getTicket();
                }
            }
        } catch (Exception e) {
            return null;
        }

        return null;
    }

    /**
     * 生日提醒
     */
    @Override
    public void sendBirthdayMention(Integer beforeDays) {
        List<BirthdayTaskDO> list = mentionDao.findBirthdayTask(beforeDays);
        for (BirthdayTaskDO bdo : list) {
            sendMessageTest(bdo);
        }

    }

    /**
     * 发送微信消息测试
     */
    public void sendMessageTest(BirthdayTaskDO bdo) {
        //此接口无需关注也能获取信息
        String token = baseParaService.getAccesstoken();
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + token;
        WeixinMessageRo ro = new WeixinMessageRo();
        ro.setTouser(bdo.getOpenId());
        ro.setTemplate_id(weixinpayConfig.getBirthDayNoticeTemplateId());
        Map<String, Object> map = new HashMap<>();
        Map<String, String> lsitMap = new HashMap<>();
        lsitMap.put("value", "尊敬的客户您好，您有一个生日提醒，具体信息如下:");
        lsitMap.put("color", "#173177");
        map.put("first", lsitMap);
        lsitMap = new HashMap<>();
        lsitMap.put("value", "孝行通");
        lsitMap.put("color", "#173177");
        map.put("keyword1", lsitMap);
        lsitMap = new HashMap<>();
        lsitMap.put("value", "生日提醒");
        lsitMap.put("color", "#173177");
        map.put("keyword2", lsitMap);
        lsitMap = new HashMap<>();
        lsitMap.put("value", "您有一个生日提醒：" + bdo.getName() + "(" + bdo.getRelation() + "），生日 " + DateUtil.dateToString("yyyy-MM-dd", bdo.getBirthday()));
        lsitMap.put("color", "#173177");
        map.put("keyword3", lsitMap);
        lsitMap = new HashMap<>();
        lsitMap.put("value", DateUtil.dateToString("yyyy.MM.dd", new Date()));
        lsitMap.put("color", "#173177");
        map.put("keyword4", lsitMap);
        lsitMap = new HashMap<>();
        lsitMap.put("value", "谢谢使用。");
        lsitMap.put("color", "#173177");
        map.put("remark", lsitMap);
        ro.setData(map);
        String id= baseParaService.getByCode("BIRTHDAY_SUBJECT_ID");
        if(StringUtils.isNotEmpty(id)){
        	ro.setUrl(subjectUrl+baseParaService.getByCode("BIRTHDAY_SUBJECT_ID"));
        }
        String json = MyGson.getInstance().toJson(ro);
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        try {
            Response response = OkHttpUtil.execute(request);
/*            Map<String, Object> map1 = GsonUtil.getGson().fromJson(response.body().string(), new TypeToken<Map<String, Object>>() {
            }.getType());*/
/*            for (String str : map.keySet()) {
                System.out.println(map1.get(str).toString());
            }*/
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
